/*
 * Copyright(c)2018,2024,guowenmeng24@163.comAllrightsreserved.
 */
package com.bkhech.home.practice.lottery;

import lombok.Data;

import java.util.*;

/**
 * TempDemo
 *
 * @author guowm
 * @date 2024/06/26
 */
public class TempDemo {
    public static void main(String[] args) {
        // 奖品列表
        List<Award> awards = new ArrayList<>();
        // 总的奖品数量
        int totalAmount = 9;
        // 总的奖品剩余数量
        int totalBalance = 6;

    }

    /**
     * 抽奖并更新上次更新时间
     *
     * @param awards
     * @param totalAmount 总的奖品数量
     * @param totalBalance 总的奖品剩余数量
     * @return
     */
    public static Award draw(List<Award> awards, int totalAmount, int totalBalance) {
        long now = System.currentTimeMillis();
        // 活动开始时间
        long startTime = new Date(now - 2 * 3600 * 1000).getTime();
        // 活动结时间
        long endTime = new Date(now + 1 * 3600 * 1000).getTime();

        /* 从所有等级奖品中选中一类奖品，剩余多的越容易命中 */
        Optional<Award> awardOptional = randomAward(awards);
        /* 奖品都已发完 */
        if (awardOptional.isEmpty()) {
            return null;
        }

        final Award award = awardOptional.get();
        long lastUpdateTime = award.getUpdateTime().getTime();
        long deltaTime = (endTime - startTime) / totalAmount;
        /* 使用lastUpdateTime作为随机因子，保证下一个奖品的随机时间对每个抽奖者一样 */
        Random rand = new Random(lastUpdateTime);
        /* 计算得到下一个奖品的释放时间点 */
        long releaseTime = startTime + (totalAmount - totalBalance) * deltaTime + Math.abs(rand.nextLong()) % deltaTime;
        /* 当前时间未到达下一个奖品的放时同点 */
        if (now < releaseTime) {
            return null;
        }

        award.setUpdateTime(new Date());
        return award;
    }

    /**
     * 随机选出下一个待释放奖品
     *
     * @param awards 奖品池
     * @return 抽中的奖品
     */
    private static Optional<Award> randomAward(List<Award> awards) {
        if (awards == null || awards.isEmpty()) {
            return Optional.empty();
        }
        int weight = 0;
        for (Award award : awards) {
            weight += award.getBalance();
        }
        if (weight == 0) {
            return Optional.empty();
        }
        /*以当前剩余数作为随机因子，确保奖品依次被选出的顺序一致，不因为不同的参与者而不同*/
        Random random = new Random(weight);
        int num = random.nextInt(weight);
        for (Award award : awards) {
            num -= award.getBalance();
            if (num < 0) {
                return Optional.of(award);
            }
        }

        throw new RuntimeException("randomAward should shoot at lest one batch!");
    }

    @Data
    public static class Award {
        private Long id;
        private Integer version;
        private Integer balance;
        private Integer amount;
        private Date updateTime;
    }

}